﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using Microsoft.Win32;
using XOS_VHD_Tools.VHD;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using MessageBox = Xceed.Wpf.Toolkit.MessageBox;
using Path = System.IO.Path;
using System.Runtime.InteropServices;
namespace XOS_VHD_Tools
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;

            InitializeComponent();
            DataContext = this;

            AvailableDrives = Environment.GetLogicalDrives().Select(t =>
                {
                    try
                    {
                        return new RealFSDir(new DirectoryInfo(t), null);
                    }
                    catch { }
                    return null;
                }).Where(t => t != null).ToArray();
        }

        void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            e.Handled = true;

            MessageBox.Show(this, e.Exception.Message, "Critical error", MessageBoxButton.OK, MessageBoxImage.Stop);
        }

        
        class RealFSDir : IDirectory
        {
            private readonly DirectoryInfo _info;

            public RealFSDir(DirectoryInfo info, IDirectory dir)
            {
                _info = info;

                ParentDirectory = dir;
            }

            public IEnumerable<IDirectory> Directories
            {
                [DebuggerHidden]
                get
                {
                    try
                    {
                        return _info.EnumerateDirectories().Select(t => new RealFSDir(t, this));
                    }
                    catch
                    {
                    }

                    return new IDirectory[0];
                }
            }

            public IEnumerable<IFile> Files
            {
                [DebuggerHidden]
                get
                {
                    try
                    {
                        return _info.EnumerateFiles().Select(t => new RealFSFile(t, this));
                    }
                    catch
                    {
                    }

                    return new IFile[0];
                }
            }

            public IEnumerable<IFSEntry> Entries
            {
                get { return Directories.Cast<IFSEntry>().Concat(Files); }
            }

            public IDirectory CreateDirectory(string name)
            {
                var di = _info.CreateSubdirectory(name);

                return new RealFSDir(di, this);
            }

            public IFile CreateFile(string name)
            {
                throw new NotImplementedException();
            }

            public void DeleteItem(IFSEntry entry)
            {
                _info.Delete(true);
            }

            public void RenameItem(IFSEntry entry, string name)
            {
                if (entry.ParentDirectory != this)
                {
                    throw new InvalidOperationException();
                }

                if (entry is RealFSFile)
                {
                    var file = entry as RealFSFile;

                    file.Rename(name);
                }
                else if (entry is RealFSDir)
                {
                    var dir = entry as RealFSDir;

                    dir.RenameInternal(name);
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }

            private void RenameInternal(string name)
            {
                // todo check
                _info.MoveTo(name);
            }

            public bool IsRootDirectory
            {
                get { return _info.Root == _info; }
            }

            public string Name
            {
                get { return _info.Name; }
            }

            public IDirectory ParentDirectory
            {
                get;
                private set;
            }
        }

        class RealFSFile : IFile
        {
            private readonly FileInfo _info;

            public RealFSFile(FileInfo info, IDirectory dir)
            {
                _info = info;
                ParentDirectory = dir;
            }

            public long Size
            {
                get { return _info.Length; }
            }

            public System.IO.Stream OpenFile()
            {
                return _info.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
            }

            public void Delete()
            {
                _info.Delete();
            }

            public void Rename(string newName)
            {
                // will it work?
                _info.MoveTo(newName);
            }

            public string Name
            {
                get { return _info.Name; }
            }

            public IDirectory ParentDirectory
            {
                get;
                private set;
            }
        }

        public IDirectory[] AvailableDrives
        {
            get { return (IDirectory[])GetValue(AvailableDrivesProperty); }
            set { SetValue(AvailableDrivesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AvailableDrives.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AvailableDrivesProperty =
            DependencyProperty.Register("AvailableDrives", typeof(IDirectory[]), typeof(MainWindow), new UIPropertyMetadata(new IDirectory[0]));

        internal VHDFile VHD
        {
            get { return (VHDFile)GetValue(VHDProperty); }
            private set { SetValue(VHDProperty, value); }
        }

        // Using a DependencyProperty as the backing store for VHD.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty VHDProperty =
            DependencyProperty.Register("VHD", typeof(VHDFile), typeof(MainWindow), new UIPropertyMetadata(null, OnVHDPropertyChanged));

        private static void OnVHDPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj != null && obj is MainWindow)
            {
                (obj as MainWindow).OnVHDPropertyChanged(e.OldValue as VHDFile, e.NewValue as VHDFile);
            }
        }

        private void OnVHDPropertyChanged(VHDFile oldValue, VHDFile newValue)
        {
            if (oldValue != null)
            {
                oldValue.Dispose();
            }

            foreach (var ctrl in __ctrlHolder.Children.OfType<GroupBox>())
            {
                ctrl.IsEnabled = newValue != null;
            }

            btnCreateFolder.IsEnabled = newValue != null;
        }


        private void OpenVHD(object sender, RoutedEventArgs e)
        {
            if (VHD != null)
            {
                if (MessageBox.Show(this, "Do you really want to close current VHD file?", "Open VHD file", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) != MessageBoxResult.Yes)
                {
                    return;
                }

                VHD = null;
            }

            var ofd = new OpenFileDialog
            {
                AddExtension = true,
                CheckFileExists = true,
                CheckPathExists = true,
                FileName = "",
                Filter = "VHD file|*.vhd",
                Multiselect = false,
                ReadOnlyChecked = false,
                RestoreDirectory = true,
                ShowReadOnly = false,
                Title = "Open an existing VHD file"
            };

            if (ofd.ShowDialog(this) == true)
            {
                IsBusy = true;

                Task.Factory.StartNew(() =>
                    {
                        try
                        {
                            var vhd = new VHDFile(ofd.FileName);

                            this.SetDependencyValue(VHDProperty, vhd);
                        }
                        catch (Exception ex)
                        {
                            this.Invoke(() =>
                                {
                                    MessageBox.Show(this, "Error occured:\n" + ex.Message, "Open VHD file", MessageBoxButton.OK, MessageBoxImage.Error);
                                });
                        }
                        finally
                        {
                            this.SetDependencyValue(IsBusyProperty, false);
                        }
                    });
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            OnVHDPropertyChanged(null, null);
        }

        public bool IsBusy
        {
            get { return (bool)GetValue(IsBusyProperty); }
            set { SetValue(IsBusyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsBusy.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsBusyProperty =
            DependencyProperty.Register("IsBusy", typeof(bool), typeof(MainWindow), new UIPropertyMetadata(false));

        private void _itHD_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            btnToVHD.IsEnabled = _itHD.SelectedItem != null;
        }

        private void _itVHD_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            btnToHD.IsEnabled = _itVHD.SelectedItem != null;
            btnDeleteItem.IsEnabled = _itVHD.SelectedItem != null &&
                _itVHD.SelectedItem is IFSEntry &&
                (_itVHD.SelectedItem as IFSEntry).ParentDirectory != null;
        }

        private void btnToVHD_Click(object sender, RoutedEventArgs e)
        {

        }

        private void btnToHD_Click(object sender, RoutedEventArgs e)
        {

        }

        private void btnDeleteItem_Click(object sender, RoutedEventArgs e)
        {
            var fsItem = _itVHD.SelectedItem as IFSEntry;

            if (fsItem != null && fsItem.ParentDirectory != null)
            {
                fsItem.ParentDirectory.DeleteItem(fsItem);
            }
        }

        private void btnCreateFolder_Click(object sender, RoutedEventArgs e)
        {
            var fsItem = _itVHD.SelectedItem as IFSEntry;

            if (fsItem != null)
            {
                if (!(fsItem is IDirectory))
                {
                    fsItem = fsItem.ParentDirectory;

                    if (fsItem == null)
                    {
                        MessageBox.Show(this, "Current item has no parent directory - this is strange!", "Create directory", MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                }

                var dir = (fsItem as IDirectory);


                EventHandler nameDlgClosedEvent = null; ;

                nameDlgClosedEvent = (s, ee) =>
                    {
                        __nameDlg.Closed -= nameDlgClosedEvent;

                        if (__nameDlg.DialogResult == true)
                        {
                            dir.CreateDirectory(_itemName.Text);

                            _itemName.Text = "";
                        }
                    };

                __nameDlg.Closed += nameDlgClosedEvent;

                __nameDlg.Show();

            }
            else
            {
                MessageBox.Show(this, "No directory is selected - I don't know where new one must be created!", "Create directory", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            __nameDlg.DialogResult = false;
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            __nameDlg.DialogResult = true;
        }

        private void _itemName_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            e.Handled = e.Text.IndexOfAny(Path.GetInvalidFileNameChars()) != -1;
        }

    }

    class IsNotStringEmptyConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value == null ? false : !string.IsNullOrWhiteSpace(value.ToString());
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    class VHDNameConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value == null || !(value is VHDFile) ? "<File is not opened>" : (value as VHDFile).FilePath;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    class FSDirectoryToRootObj : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null)
            {
                if (value is IEnumerable<VHDPartition>)
                {
                    return (value as IEnumerable<VHDPartition>).Select(t =>
                        {
                            try
                            {
                                return t.Driver;
                            }
                            catch
                            { }

                            return null;
                        }).Where(t => t != null).Select(t => t.RootDirectory);
                }
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    class FSEntryToImageSource : IValueConverter
    {
        private static readonly BitmapImage _dir = new BitmapImage(new Uri("pack://application:,,,/XOS_VHD_Tools;component/Images/folder.png"));
        private static readonly BitmapImage _app = new BitmapImage(new Uri("pack://application:,,,/XOS_VHD_Tools;component/Images/app.png"));
        private static readonly BitmapImage _file = new BitmapImage(new Uri("pack://application:,,,/XOS_VHD_Tools;component/Images/file.png"));

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null)
            {
                if (value is IDirectory)
                {
                    return (value as IDirectory).Name.EndsWith(".APP", StringComparison.CurrentCultureIgnoreCase) ?
                        _app : _dir;
                }
                else if (value is IFile)
                {
                    return _file;
                }
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
